package ws

import (
	"github.com/gin-gonic/gin"
	"github.com/gofrs/uuid"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"gobase/logger"
	"net/http"
	"sync"
	"time"
)

// WebsocketManager 初始化 WebsocketManager 管理器
var WebsocketManager = Manager{
	Group:            make(map[string]map[string]*Client),
	Register:         make(chan *Client, 128),
	UnRegister:       make(chan *Client, 128),
	GroupMessage:     make(chan *GroupMessageData, 128),
	Message:          make(chan *MessageData, 128),
	BroadCastMessage: make(chan *BroadCastMessageData, 128),
	groupCount:       0,
	clientCount:      0,
}

// Manager 所有 websocket 信息
type Manager struct {
	Group                   map[string]map[string]*Client
	groupCount, clientCount uint
	Lock                    sync.Mutex
	Register, UnRegister    chan *Client
	Message                 chan *MessageData
	GroupMessage            chan *GroupMessageData
	BroadCastMessage        chan *BroadCastMessageData
}

// Start 启动 websocket 管理器
func (manager *Manager) Start() {
	logger.Info("websocket manage start")
	for {
		select {
		// 注册
		case client := <-manager.Register:
			logger.Info("client connect", zap.String("id", client.Id))
			logger.Info("register client to group", zap.String("id", client.Id), zap.String("group", client.Group))

			manager.Lock.Lock()
			if manager.Group[client.Group] == nil {
				manager.Group[client.Group] = make(map[string]*Client)
				manager.groupCount += 1
			}
			manager.Group[client.Group][client.Id] = client
			manager.clientCount += 1
			manager.Lock.Unlock()

		// 注销
		case client := <-manager.UnRegister:
			manager.Lock.Lock()
			if g, ok := manager.Group[client.Group]; ok {
				if _, ok := g[client.Id]; ok {
					logger.Info("注销 ws 链接", zap.String("group", client.Group), zap.String("id", client.Id))
					manager.Group[client.Group][client.Id].StopConn <- struct{}{}

					delete(manager.Group[client.Group], client.Id)
					close(client.SendMessage)
					close(client.ReadMessage)

					manager.clientCount -= 1
					if len(manager.Group[client.Group]) == 0 {
						logger.Info("delete empty group", zap.String("group", client.Group))
						delete(manager.Group, client.Group)
						manager.groupCount -= 1
					}
				}
			}
			manager.Lock.Unlock()
		}
	}
}

// SendService 处理单个 client 发送数据
func (manager *Manager) SendService() {
	for {
		select {
		case data := <-manager.Message:
			if groupMap, ok := manager.Group[data.Group]; ok {
				if conn, ok := groupMap[data.Id]; ok {
					conn.SendMessage <- data.Message
				}
			}
		}
	}
}

// SendGroupService 处理 group 广播数据
func (manager *Manager) SendGroupService() {
	for {
		select {
		// 发送广播数据到某个组的 channel 变量 Send 中
		case data := <-manager.GroupMessage:
			if groupMap, ok := manager.Group[data.Group]; ok {
				for _, conn := range groupMap {
					conn.SendMessage <- data.Message
				}
			}
		}
	}
}

// SendAllService 处理广播数据
func (manager *Manager) SendAllService() {
	for {
		select {
		case data := <-manager.BroadCastMessage:
			for _, v := range manager.Group {
				for _, conn := range v {
					conn.SendMessage <- data.Message
				}
			}
		}
	}
}

// Send 向指定的 client 发送数据
func (manager *Manager) Send(id string, group string, message []byte) {
	data := &MessageData{
		Id:      id,
		Group:   group,
		Message: message,
	}
	manager.Message <- data
}

// SendGroup 向指定的 Group 广播
func (manager *Manager) SendGroup(group string, message []byte) {
	data := &GroupMessageData{
		Group:   group,
		Message: message,
	}
	manager.GroupMessage <- data
}

// SendAll 广播
func (manager *Manager) SendAll(message []byte) {
	data := &BroadCastMessageData{
		Message: message,
	}
	manager.BroadCastMessage <- data
}

// RegisterClient 注册
func (manager *Manager) RegisterClient(client *Client) {
	manager.Register <- client
}

// UnRegisterClient 注销
func (manager *Manager) UnRegisterClient(client *Client) {
	manager.UnRegister <- client
}

// LenGroup 当前组个数
func (manager *Manager) LenGroup() uint {
	return manager.groupCount
}

// LenClient 当前连接个数
func (manager *Manager) LenClient() uint {
	return manager.clientCount
}

// Info 获取 WebsocketManager 管理器信息
func (manager *Manager) Info() map[string]interface{} {
	managerInfo := make(map[string]interface{})
	managerInfo["groupLen"] = manager.LenGroup()
	managerInfo["clientLen"] = manager.LenClient()
	managerInfo["chanRegisterLen"] = len(manager.Register)
	managerInfo["chanUnregisterLen"] = len(manager.UnRegister)
	managerInfo["chanMessageLen"] = len(manager.Message)
	managerInfo["chanGroupMessageLen"] = len(manager.GroupMessage)
	managerInfo["chanBroadCastMessageLen"] = len(manager.BroadCastMessage)
	return managerInfo
}

// WsConnect gin 处理 websocket handler
func (manager *Manager) WsConnect(ctx *gin.Context) {
	upGrader := websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		// cross origin domain1`
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
		// 处理 Sec-WebSocket-Protocol Header
		Subprotocols: []string{ctx.GetHeader("Sec-WebSocket-Protocol")},
	}

	conn, err := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)
	if err != nil {
		logger.Error("websocket connect error", zap.Error(err), zap.String("channel", ctx.Param("channel")))
		return
	}

	uid, _ := uuid.NewV4()
	client := &Client{
		Id:          uid.String(),
		StopConn:    make(chan struct{}, 1),
		Group:       ctx.Param("tid"),
		Socket:      conn,
		ReadMessage: make(chan []byte, 1024),
		SendMessage: make(chan []byte, 1024),
	}

	if manager.Group[client.Group] == nil {
		manager.Group[client.Group] = make(map[string]*Client)
		manager.groupCount += 1
	}
	manager.Group[client.Group][client.Id] = client
	manager.clientCount += 1
	//manager.RegisterClient(client)
	go client.Read()
	go client.Write()

	//读数据  test
	go func() {
		for {
			select {
			case <-WebsocketManager.Group[client.Group][client.Id].StopConn:
				logger.Warn("ws_服务结束", zap.Any("uuId", uid))
				return
			case readMessage := <-client.ReadMessage:
				manager.SendGroup(client.Group, readMessage)
			}
		}
	}()

	// 测试单个 client 发送数据
	manager.Send(client.Id, client.Group, []byte("Send message ----"+time.Now().Format("2006-01-02 15:04:05")))
	//
	manager.SendAll([]byte("Send All message ----" + time.Now().Format("2006-01-02 15:04:05")))
}
